package middleware

import (
	jwt "github.com/appleboy/gin-jwt/v2"
	"github.com/gin-gonic/gin"
	"interview/models"
	"interview/pkg/common"
	"interview/pkg/global"
	"interview/pkg/request"
	"interview/pkg/response"
	"interview/pkg/service/basesrv"
	"interview/pkg/utils"
	"time"
)

func InitJwtAuth() (*jwt.GinJWTMiddleware, error) {
	return jwt.New(&jwt.GinJWTMiddleware{
		Realm:           global.Conf.Jwt.Realm,
		Key:             []byte(global.Conf.Jwt.Key),
		Timeout:         time.Hour * time.Duration(global.Conf.Jwt.Timeout),
		MaxRefresh:      time.Hour * time.Duration(global.Conf.Jwt.MaxRefresh),
		PayloadFunc:     payloadFunc,     // 有效载荷处理
		IdentityHandler: identityHandler, // 解析Claims
		Authenticator:   authenticator,   // 校验token的正确性
		Authorizator:    authorizator,    // 用户登录校验成功处理
		Unauthorized:    unauthorized,    // 用户登录校验失败处理
		LoginResponse:   loginResponse,   // 登录成功后的响应
		LogoutResponse:  logoutResponse,  // 登出后的响应
		// TokenLookup is a string in the form of "<source>:<name>" that is used
		// to extract token from the request.
		// Optional. Default value "header:Authorization".
		TokenLookup: global.Conf.Jwt.TokenLookup,

		// TokenHeadName is a string in the header. Default value is "Bearer"
		TokenHeadName: global.Conf.Jwt.TokenHeadName,

		// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
		TimeFunc: time.Now,
	})
}

// 有效载荷处理
func payloadFunc(data interface{}) jwt.MapClaims {
	if v, ok := data.(map[string]interface{}); ok {
		var user models.User
		utils.JsonI2Struct(v["user"], &user)
		return jwt.MapClaims{
			jwt.IdentityKey: user.ID,
			"user":          v["user"],
		}
	}
	return jwt.MapClaims{}
}

// 解析Claims
func identityHandler(c *gin.Context) interface{} {
	claims := jwt.ExtractClaims(c)
	// 此处返回值类型map[string]interface{}与payloadFunc和authorizator的data类型必须一致
	return map[string]interface{}{
		"IdentityKey": claims[jwt.IdentityKey],
		"user":        claims["user"],
	}
}

// 校验token的正确性
func authenticator(c *gin.Context) (interface{}, error) {
	s := basesrv.NewMysqlService(c)
	var req request.RegisterAndLoginUserReq
	// 校验token异常
	if err := c.ShouldBind(&req); err != nil {
		return "", jwt.ErrMissingLoginValues
	}
	u := &models.User{
		Username: req.Username,
		Password: req.Password,
	}
	// 密码校验
	user, err := s.LoginCheck(u)
	if err != nil {
		return nil, err
	}

	// 写入JSON格式用户信息
	return map[string]interface{}{
		"user": utils.Struct2JsonString(user),
	}, nil
}

// 校验用户的正确性
func authorizator(data interface{}, c *gin.Context) bool {
	if v, ok := data.(map[string]interface{}); ok {
		var user models.User
		utils.JsonI2Struct(v["user"], &user)
		// 用户保存到context方便api调用
		c.Set("user", user)
		return true
	}

	return false
}

func unauthorized(c *gin.Context, code int, message string) {
	global.Logger.Debugf("JWT认证失败，错误码%d，错误信息%s\n", code, message)
	if message == common.LoginCheckErrMsg {
		response.FailWithMsg(common.LoginCheckErrMsg)
		c.Abort()
		return
	}
	response.FailWithCode(common.Unauthorized)
}

func loginResponse(c *gin.Context, code int, token string, expires time.Time) {
	response.SuccessWithCodeData(code, map[string]interface{}{
		"token":   token,
		"expires": expires,
	})
}

func logoutResponse(c *gin.Context, code int) {
	response.SuccessWithCode(code, "退出登录成功")
}
